//*******************************************************************       //
//IMPORTANT NOTICE                                                          //
//================                                                          //
//Copyright Mentor Graphics Corporation 1996 - 1999.  All rights reserved.  //
//This file and associated deliverables are the trade secrets,              //
//confidential information and copyrighted works of Mentor Graphics         //
//Corporation and its licensors and are subject to your license agreement   //
//with Mentor Graphics Corporation.                                         //
//                                                                          //
//Use of these deliverables for the purpose of making silicon from an IC    //
//design is limited to the terms and conditions of your license agreement   //
//with Mentor Graphics If you have further questions please contact Mentor  //
//Graphics Customer Support.                                                //
//                                                                          //
//This Mentor Graphics core (m8051w v2002.080) was extracted on             //
//workstation hostid 8316cbec Inventra                                      //
// Interrupt Controller for M8051W/EW
// 
// $Log: m3s015dy.v,v $
// Revision 1.13  2001/11/20
// First checkin of version 2 features and name change
//
// Revision 1.2  2001/10/31
// First parsable verilog for EW
//
// Revision 1.1.1.1  2001/07/17
// Re-imported E-Warp from Farnham filesystem
//
// Revision 1.12  2000/10/24
// Multiplier rewritten to improve power consumption.
// Code changes for Leonardo (ECN01372).
// Code changes for formal verification tools (ECN01410).
// MOVX @Ri page address controllable from PORT2I if I/O ports ommitted (ECN01387).
//
// Revision 1.11  2000/02/15
// Renamed extended interrupts
//
// Revision 1.10  2000/02/07
// IP0H write enable fixed. IP1H register redundancy removed.
//
// Revision 1.9  2000/02/05
// Name change repercussions
//
// Revision 1.8  2000/01/06
// PROGA no longer cleared during idle mode.
// Minor code tidying up.
//
// Revision 1.7  1999/12/08
// Eliminate user instruction during state transition from interrupt service to
// debug state.
// RTL clean up of redundant declarations and sensitvity list entries.
//
// Revision 1.6  1999/12/07
// *** empty log message ***
//
// Revision 1.5  1999/11/30
// More debug changes.
//
// Revision 1.4  1999/11/18
// SFR read enable added, TRAP and DPTR fixes.
//
// Revision 1.3  1999/11/04
// Sensitivity list corrections.
//
// Revision 1.2  1999/11/03
// Debug mode revisions.
//
// Revision 1.1.1.1  1999/10/28
// "initialization and source check-in for m8051e"
//
// Revision 1.1  1999/10/22
// Initial revision
//
////////////////////////////////////////////////////////////////////////////////

`include "m8051w_defs.v"
`include "m8051w_cfg.v"

module m3s015dy (INTR_SFR_DATA, INTR_VECTOR, INTR_ACKNOW, NMI_ACK, EXCEPTION,
//*******************************************************************       //
//IMPORTANT NOTICE                                                          //
//================                                                          //
//Copyright Mentor Graphics Corporation 1996 - 1999.  All rights reserved.  //
//This file and associated deliverables are the trade secrets,              //
//confidential information and copyrighted works of Mentor Graphics         //
//Corporation and its licensors and are subject to your license agreement   //
//with Mentor Graphics Corporation.                                         //
//                                                                          //
//Use of these deliverables for the purpose of making silicon from an IC    //
//design is limited to the terms and conditions of your license agreement   //
//with Mentor Graphics If you have further questions please contact Mentor  //
//Graphics Customer Support.                                                //
//                                                                          //
//This Mentor Graphics core (m8051w v2002.080) was extracted on             //
//workstation hostid 8316cbec Inventra                                      //
                 INTR_LCALL, DebugIUS,
                 INTR_SOURCE, NMI, REG_RESULT, SFR_S_ADDR, DESTIN_ADDR, 
                 SFR_WRITE, DebugHold,
		 INTR_RET, STATE, LAST_CYC, PCLK_EN, INTERNAL_WAIT, SCLK, CCLK,
                 INTERNAL_RESET);

  output [7:0]   INTR_SFR_DATA;
  output [7:0]   INTR_VECTOR;
  output [13:0]  INTR_ACKNOW;
  output         NMI_ACK;
  output         EXCEPTION;
  output         INTR_LCALL;
  output         DebugIUS;

  input  [13:0]  INTR_SOURCE;
  input          NMI;
  input  [7:0]   REG_RESULT;
  input  [6:0]   SFR_S_ADDR;
  input  [6:0]   DESTIN_ADDR;
  input          SFR_WRITE;
  input          DebugHold;
  input  [0:0]   STATE;
  input          LAST_CYC;
  input          INTR_RET;
  input          PCLK_EN;
  input          INTERNAL_WAIT;
  input          SCLK, CCLK, INTERNAL_RESET;

  reg    [7:0]   INTR_EN0, INTR_PRI0;
  reg    [4:0]   SERVICE_LEVEL, REQUEST_LEVEL;
  reg            ACKNOWLEDGE, INTR_LCALL;
  reg    [13:0]  SOURCES;          // registered interrupt sources
  reg            NMI_SOURCE;
  reg    [13:0]  EXCEPTION_CODE;
  reg    [13:0]  HIGHEST_CODE;
  reg    [7:0]   INTR_VECTOR;
  reg    [7:0]   INTR_SFR_DATA;

  wire   [7:0]   W_INTR_EN0, W_INTR_PRI0;
  wire   [7:0]   W_INTR_EN1, W_INTR_PRI1;
  wire   [7:0]   W_INTR_PRI0H, W_INTR_PRI1H;
  wire   [13:0]  LEVEL_3_SOURCES, LEVEL_2_SOURCES;
  wire   [13:0]  LEVEL_1_SOURCES, LEVEL_0_SOURCES;
  wire           EXCEPTION, BLOCK_EXCEPTION;
  wire   [13:0]  INTR_ACKNOW;

  wire           IE0_WRITE;
  wire           IP0_WRITE;
  wire           IE1_WRITE;
  wire           IP1_WRITE;
  wire           IP0H_WRITE;
  wire           IP1H_WRITE;

  // SFR Write Strobes

  assign IE0_WRITE  = SFR_WRITE && DESTIN_ADDR == `AddrIE;
  assign IP0_WRITE  = SFR_WRITE && DESTIN_ADDR == `AddrIP;
  assign IE1_WRITE  = SFR_WRITE && DESTIN_ADDR == `AddrIE1;
  assign IP1_WRITE  = SFR_WRITE && DESTIN_ADDR == `AddrIP1;
  assign IP0H_WRITE = SFR_WRITE && DESTIN_ADDR == `AddrIPH;
  assign IP1H_WRITE = SFR_WRITE && DESTIN_ADDR == `AddrIP1H;


  // Standard Interrupt Controller Special Function Registers

  always @(posedge CCLK)
  begin: p_interrupt_SFRs
    if (INTERNAL_RESET) begin
      INTR_EN0  <= 8'h00;
      INTR_PRI0 <= 8'h00;
    end
    else begin
      if (IE0_WRITE) INTR_EN0  <= REG_RESULT;
      if (IP0_WRITE) INTR_PRI0 <= REG_RESULT;
    end
  end

  assign W_INTR_EN0  = INTR_EN0;
  assign W_INTR_PRI0 = INTR_PRI0;
 
  // Extended Interrupt Source Special Function Registers

`ifdef ExtraInterrupts
  reg    [7:0]   INTR_EN1, INTR_PRI1;
  
  always @(posedge CCLK)
  begin: p_extra_interrupt_SFRs
    if (INTERNAL_RESET) begin
      INTR_EN1  <= 8'h00;
      INTR_PRI1 <= 8'h00;
    end
    else begin
      if (IE1_WRITE) INTR_EN1  <= REG_RESULT;
      if (IP1_WRITE) INTR_PRI1 <= REG_RESULT;
    end
  end

  assign W_INTR_EN1  = INTR_EN1;
  assign W_INTR_PRI1 = INTR_PRI1;

`else

  assign W_INTR_EN1  = 0;
  assign W_INTR_PRI1 = 0;

`endif
 
  // Extra Priority Special Function Registers

`ifdef ExtraPriorities
  `ifdef ExtraInterrupts
    reg    [7:0]   INTR_PRI0H, INTR_PRI1H;

    always @(posedge CCLK)
    begin: p_hi_pri_exin_SFRs
      if (INTERNAL_RESET) begin
        INTR_PRI0H <= 8'h00;
        INTR_PRI1H <= 8'h00;
      end
      else begin
        if (IP0H_WRITE) INTR_PRI0H <= REG_RESULT;
        if (IP1H_WRITE) INTR_PRI1H <= REG_RESULT;
      end
    end

    assign W_INTR_PRI0H = INTR_PRI0H;
    assign W_INTR_PRI1H = INTR_PRI1H;

    // Generate Interrupt Blocking Signal
    // INTR_RET indicates the current opcode is for the RETI instruction.

    assign BLOCK_EXCEPTION = IE0_WRITE  || IE1_WRITE  ||
                             IP0_WRITE  || IP1_WRITE  ||
			     IP0H_WRITE || IP1H_WRITE ||
			     INTR_RET;

  `else
    reg    [7:0]   INTR_PRI0H;

    always @(posedge CCLK)
    begin: p_hi_pri_stdin_SFRs
      if (INTERNAL_RESET) begin
        INTR_PRI0H <= 8'h00;
      end
      else begin
        if (IP0H_WRITE) INTR_PRI0H <= REG_RESULT;
      end
    end

    assign W_INTR_PRI0H = INTR_PRI0H;
    assign W_INTR_PRI1H = 0;

    assign BLOCK_EXCEPTION = IE0_WRITE  || IP0_WRITE  || IP0H_WRITE || INTR_RET;
  `endif
`else

  assign W_INTR_PRI0H = 0;
  assign W_INTR_PRI1H = 0;

  `ifdef ExtraInterrupts

    assign BLOCK_EXCEPTION = IE0_WRITE  || IE1_WRITE  || IP0_WRITE  ||
                             IP1_WRITE  || INTR_RET;

  `else

    assign BLOCK_EXCEPTION = IE0_WRITE  || IP0_WRITE  || INTR_RET;

  `endif
`endif

  // Poll the enabled interrupt sources at end of CLP1

  always @(posedge SCLK)
  begin: p_interrupt_poll
    if (INTERNAL_RESET) begin
      NMI_SOURCE <= 0;
      SOURCES <= 0;
    end
    else if (LAST_CYC && !PCLK_EN && !INTERNAL_WAIT) begin
      NMI_SOURCE <= NMI;
      SOURCES <= INTR_SOURCE & {W_INTR_EN1, W_INTR_EN0[5:0]} &
                 {14{W_INTR_EN0[7]}};
    end
  end

  // Separate interrupt sources by priority level

  assign LEVEL_3_SOURCES = SOURCES &  {W_INTR_PRI1,  W_INTR_PRI0[5:0]} &
                                      {W_INTR_PRI1H, W_INTR_PRI0H[5:0]};
  assign LEVEL_2_SOURCES = SOURCES & ~{W_INTR_PRI1,  W_INTR_PRI0[5:0]} &
                                      {W_INTR_PRI1H, W_INTR_PRI0H[5:0]};
  assign LEVEL_1_SOURCES = SOURCES &  {W_INTR_PRI1,  W_INTR_PRI0[5:0]} &
                                     ~{W_INTR_PRI1H, W_INTR_PRI0H[5:0]};
  assign LEVEL_0_SOURCES = SOURCES & ~{W_INTR_PRI1,  W_INTR_PRI0[5:0]} &
                                     ~{W_INTR_PRI1H, W_INTR_PRI0H[5:0]};

  // Establish level of highest priority source and separate highest level
  // sources for subsequent settlement of priority within level

  always @(NMI_SOURCE or LEVEL_3_SOURCES or LEVEL_2_SOURCES or LEVEL_1_SOURCES
           or LEVEL_0_SOURCES)
  begin: p_code_exception
    if (NMI_SOURCE) begin
      REQUEST_LEVEL <= 5'b10000;
      EXCEPTION_CODE <= 0;
    end
    else if (|LEVEL_3_SOURCES) begin
           REQUEST_LEVEL <= 5'b01000;
           EXCEPTION_CODE <= LEVEL_3_SOURCES;
         end
         else if (|LEVEL_2_SOURCES) begin
                REQUEST_LEVEL <= 5'b00100;
                EXCEPTION_CODE <= LEVEL_2_SOURCES;
              end
              else if (|LEVEL_1_SOURCES) begin
                     REQUEST_LEVEL <= 5'b00010;
                     EXCEPTION_CODE <= LEVEL_1_SOURCES;
                   end
                   else if (|LEVEL_0_SOURCES) begin
                          REQUEST_LEVEL <= 5'b00001;
                          EXCEPTION_CODE <= LEVEL_0_SOURCES;
                        end
                        else begin
                          REQUEST_LEVEL <= 0;
                          EXCEPTION_CODE <= 0;
                        end
  end
  
  // Generate an exception from any enabled interrupt if not blocked.
  // Any Debug state blocks generation of a new exception.
  
  assign EXCEPTION = LAST_CYC && PCLK_EN && !BLOCK_EXCEPTION && !DebugHold &&
                       ~SERVICE_LEVEL[4] && (REQUEST_LEVEL[4] ||
                        (~SERVICE_LEVEL[3] && (REQUEST_LEVEL[3] || 
                          (~SERVICE_LEVEL[2] && (REQUEST_LEVEL[2] || 
                            (~SERVICE_LEVEL[1] && (REQUEST_LEVEL[1] || 
                              (~SERVICE_LEVEL[0] && REQUEST_LEVEL[0]))))))));
  
  // Register the interrupt state at the end of phase 1 of each final m/c cycle
  // With the exception that a priority one interrupt may interrupt a priority
  // zero interrupt in service, an interrupt in service cannot be interrupted.
  
  always @(posedge SCLK)
  begin: p_interrupt_state
    if (INTERNAL_RESET)
      SERVICE_LEVEL <= 0;
    else if (LAST_CYC && PCLK_EN && !INTERNAL_WAIT) begin
      if (INTR_RET) begin
        SERVICE_LEVEL[4] <= 1'b0;
        SERVICE_LEVEL[3] <= SERVICE_LEVEL[3] && |SERVICE_LEVEL[4];
        SERVICE_LEVEL[2] <= SERVICE_LEVEL[2] && |SERVICE_LEVEL[4:3];
        SERVICE_LEVEL[1] <= SERVICE_LEVEL[1] && |SERVICE_LEVEL[4:2];
        SERVICE_LEVEL[0] <= SERVICE_LEVEL[0] && |SERVICE_LEVEL[4:1];
      end
      if (EXCEPTION) begin 
        SERVICE_LEVEL[4] <= SERVICE_LEVEL[4] || REQUEST_LEVEL[4];
        SERVICE_LEVEL[3] <= SERVICE_LEVEL[3] || (REQUEST_LEVEL[3] &&
                                                   ~SERVICE_LEVEL[4]);
        SERVICE_LEVEL[2] <= SERVICE_LEVEL[2] || (REQUEST_LEVEL[2] &&
                                                   ~|SERVICE_LEVEL[4:3]);
        SERVICE_LEVEL[1] <= SERVICE_LEVEL[1] || (REQUEST_LEVEL[1] &&
                                                   ~|SERVICE_LEVEL[4:2]);
        SERVICE_LEVEL[0] <= SERVICE_LEVEL[0] || (REQUEST_LEVEL[0] &&
                                                   ~|SERVICE_LEVEL[4:1]);
      end // exception
    end // last cyc
  end // process
  
  // Generate a two-clock period acknowledge strobe following each exception.
  // Set flag for exception LCALL instruction to signal modified program counter
  // behaviour.
  
  always @(posedge SCLK)
  begin: p_ack
    if (INTERNAL_RESET) begin
      ACKNOWLEDGE <= 0;
      INTR_LCALL <= 0;
    end
    else begin
      if (PCLK_EN && !INTERNAL_WAIT)
        ACKNOWLEDGE <= EXCEPTION;
      if (EXCEPTION && !INTERNAL_WAIT)
        INTR_LCALL <= 1;
      else if (`CLP2 && !INTERNAL_WAIT)
        INTR_LCALL <= 0;
    end
  end
  
  // Priority encode the highest intra-level interrupt.

  always @(EXCEPTION_CODE)
  begin: p_resolve_highest
    HIGHEST_CODE[ 0] = EXCEPTION_CODE[ 0];
    HIGHEST_CODE[ 1] = EXCEPTION_CODE[ 1] && !EXCEPTION_CODE[0];
    HIGHEST_CODE[ 2] = EXCEPTION_CODE[ 2] && ~|EXCEPTION_CODE[ 1:0];
    HIGHEST_CODE[ 3] = EXCEPTION_CODE[ 3] && ~|EXCEPTION_CODE[ 2:0];
    HIGHEST_CODE[ 4] = EXCEPTION_CODE[ 4] && ~|EXCEPTION_CODE[ 3:0];
    HIGHEST_CODE[ 5] = EXCEPTION_CODE[ 5] && ~|EXCEPTION_CODE[ 4:0];
    HIGHEST_CODE[ 6] = EXCEPTION_CODE[ 6] && ~|EXCEPTION_CODE[ 5:0];
    HIGHEST_CODE[ 7] = EXCEPTION_CODE[ 7] && ~|EXCEPTION_CODE[ 6:0];
    HIGHEST_CODE[ 8] = EXCEPTION_CODE[ 8] && ~|EXCEPTION_CODE[ 7:0];
    HIGHEST_CODE[ 9] = EXCEPTION_CODE[ 9] && ~|EXCEPTION_CODE[ 8:0];
    HIGHEST_CODE[10] = EXCEPTION_CODE[10] && ~|EXCEPTION_CODE[ 9:0];
    HIGHEST_CODE[11] = EXCEPTION_CODE[11] && ~|EXCEPTION_CODE[10:0];
    HIGHEST_CODE[12] = EXCEPTION_CODE[12] && ~|EXCEPTION_CODE[11:0];
    HIGHEST_CODE[13] = EXCEPTION_CODE[13] && ~|EXCEPTION_CODE[12:0];
  end

  // Generate the vector for the highest priority source.

  always @(HIGHEST_CODE or REQUEST_LEVEL)
  begin: p_proc_vector
    if (REQUEST_LEVEL[4])
      INTR_VECTOR = 8'h73;
    else
      case(HIGHEST_CODE)
        14'b00000000000001: INTR_VECTOR = 8'h03;
        14'b00000000000010: INTR_VECTOR = 8'h0B;
        14'b00000000000100: INTR_VECTOR = 8'h13;
        14'b00000000001000: INTR_VECTOR = 8'h1B;
        14'b00000000010000: INTR_VECTOR = 8'h23;
        14'b00000000100000: INTR_VECTOR = 8'h2B;
        14'b00000001000000: INTR_VECTOR = 8'h33;
        14'b00000010000000: INTR_VECTOR = 8'h3B;
        14'b00000100000000: INTR_VECTOR = 8'h43;
        14'b00001000000000: INTR_VECTOR = 8'h4B;
        14'b00010000000000: INTR_VECTOR = 8'h53;
        14'b00100000000000: INTR_VECTOR = 8'h5B;
        14'b01000000000000: INTR_VECTOR = 8'h63;
        14'b10000000000000: INTR_VECTOR = 8'h6B;
        default:            INTR_VECTOR = 8'h00;
      endcase
  end

  // Gate highest code with timing pulse to produce a clean acknowledge strobe.

  assign INTR_ACKNOW = HIGHEST_CODE & {14{ACKNOWLEDGE && ~REQUEST_LEVEL[4]}};
  assign NMI_ACK = REQUEST_LEVEL[4] & ACKNOWLEDGE;

  // SFR Data Multiplexer

  always @(W_INTR_EN0 or W_INTR_EN1 or W_INTR_PRI0 or W_INTR_PRI1 or
           W_INTR_PRI0H or W_INTR_PRI1H or SFR_S_ADDR)
  begin: p_sfr_mux
    case(SFR_S_ADDR)
      `AddrIE:   INTR_SFR_DATA = W_INTR_EN0;
      `AddrIP:   INTR_SFR_DATA = W_INTR_PRI0;
      `AddrIE1:  INTR_SFR_DATA = W_INTR_EN1;
      `AddrIP1:  INTR_SFR_DATA = W_INTR_PRI1;
      `AddrIPH:  INTR_SFR_DATA = W_INTR_PRI0H;
      `AddrIP1H: INTR_SFR_DATA = W_INTR_PRI1H;
      default: INTR_SFR_DATA = 0;
    endcase
  end

  // Interrupt services levels that block entry to debug mode

  assign DebugIUS = SERVICE_LEVEL[4] && !INTR_RET;  // NMI only

endmodule
